(module bag-from-table mzscheme
  
  (require "../private/require.ss")
  (require-class)
  (require-contracts)
  (require-etc)
  
  (require "../private/method.ss"
           "../iterator/iterator-interface.ss"
           "../table/table-interface.ss"
           "bag-interface.ss"
           "abstract-bag.ss")

  (provide/contract
   [bag-from-table% (implementation?/c bag<%>)]
   [make-bag-from-table (table/c . -> . bag/c)])

  (define-syntax (define/bag stx)
    (syntax-case stx ()
      [(_ . REST)
       #'(define/export override bag- . REST)]))

  (define (make-bag-from-table table)
    (new bag-from-table% [table table]))  
  
  (define bag-from-table%
    (class* abstract-bag% (bag<%>)
      (super-new)
      
      (init-field table)
      
      (define/private (copy/table table)
        (new bag-from-table% [table table]))
      
      (define/bag (alist)
        (send table alist))
      
      (define/bag (elements)
        (send table keys))
      
      (define/bag (clear)
        (copy/table (send table clear)))
      
      (define/bag (empty?)
        (send table empty?))
      
      (define/bag (insert/count elem count)
        (if (positive? count)
            (send table lookup elem
                  (lambda ()
                    (copy/table (send table insert elem count)))
                  (lambda (c)
                    (copy/table (send table insert elem (+ c count)))))
            this))
    
      (define/bag (iterator)
        (make-counted-from-indexed (send table iterator)))
      
      (define/bag lookup/count
        (opt-lambda (elem [failure (constant #f)] [success (lambda (e c) e)])
          (send table lookup/key elem failure success)))
      
      (define/bag (remove/count elem count)
        (if (positive? count)
            (send table lookup elem
                  (lambda () (copy/table table))
                  (lambda (c)
                    (if (<= c count)
                        (copy/table (send table remove elem))
                        (copy/table (send table insert elem (- c count))))))
            this))
      
      (define/bag (select/count)
        (send table select))
      
      (define/bag (remove/all elem)
        (copy/table (send table remove elem)))

      (define/bag (sexp)
        (send table sexp))
      
      ))
  
  (define (make-counted-from-indexed iter)
    (new counted-from-indexed-iterator% [iter iter]))
  
  (define counted-from-indexed-iterator%
    (class* object% (counted-iterator<%>)
      (super-new)
      
      (init-field iter)
      
      (define/private (copy/iter iter)
        (new counted-from-indexed-iterator% [iter iter]))
      
      (define/public (end?)
        (send iter end?))
      
      (define/public (next)
        (copy/iter (send iter next)))
      
      (define/public (element)
        (send iter key))
      
      (define/public (count)
        (send iter element))
      
      ))
  
 )